import queue
import threading
import struct
import time
from pathlib import Path
import cv2
import PIL.Image as Image

from PyQt5 import QtWidgets, QtCore
from PyQt5.QtGui import QPixmap, QMouseEvent, QPalette, QColor, QImage
from PyQt5.QtWidgets import *
from PyQt5.QtCore import Qt, QStringListModel, pyqtSignal, QTimer

from UI.main_form_ui import Ui_MainWindow
from Network.UDP import UdpLogic
from src.Network import StopThreading

from utils.networkutil import *
from utils.command_utils import *
from utils.excel_utils import *
from utils.IotDB_utils import *

import sys


class Ui_Main(QtWidgets.QMainWindow, Ui_MainWindow, UdpLogic):

    # 初始化函数
    def __init__(self):
        super(Ui_Main, self).__init__()

        self.recv_h264_thread_test = None
        self.udp_thread_recv_h264_thermal = None
        self.flyData_filePath = " "
        self.setupUi(self)

        self.FileIndex = 0
        self.FileIndex_thermal = 0
        self.show_FileIndex = 0
        self.show_FileIndex_thermal = 0
        self.current_picture_path = ""
        self.current_picture_thermal_path = ""

        self.mcm_id = -1

        self.event_saved = threading.Event()
        self.event_thermal_saved = threading.Event()
        self.condition_saved = threading.Condition()
        self.condition_saved_thermal = threading.Condition()
        self.condition_show_h264 = threading.Condition()
        self.condition_show_thermal_h264 = threading.Condition()

        # 发送接收状态
        self.send_state = None
        self.recv_state = None
        self.recv_h264_state = None
        self.recv_h264_thremal_state = None
        # 状态回报数据、飞控数据、图像数据队列
        self.command_queue_test = queue.Queue()
        self.flyData_queue_test = queue.Queue()
        self.picture_queue_test = queue.Queue()

        # 图像原始数据
        self.video_data_queue = queue.Queue()
        self.video_thermal_data_queue = queue.Queue()

        # 96字节信息队列
        self.data_96bytes_rgb_queue = queue.Queue()
        self.data_96bytes_thermal_queue = queue.Queue()

        # 所有96字节接收数量
        self.count_all_96pkgs = 0

        self.count_16bytes = 0
        self.current_rgb_pkgs_remind = 0
        self.current_thermal_pkgs_remind = 0

        self.rgb_target_nums = 0
        self.thermal_target_nums = 0
        self.is_target_info = False

        # 飞控数据解析及保存线程启动 TODO 加入中断关闭，更新保存文件
        # self.saveData_th = threading.Thread(target=self.dataParser_flyData)
        # self.saveData_th.start()
        # 状态回报数据解析线程启动
        self.command_th = threading.Thread(target=self.dataParser_command)
        self.command_th.start()
        self.saveH264_th = threading.Thread(target=self.dataParser_rgb_picture)
        self.saveH264_th.start()
        self.saveH264_thremal_th = threading.Thread(target=self.dataParser_thermal_picture)
        self.saveH264_thremal_th.start()
        self.showH264_th = threading.Thread(target=self.show_H264_picture)
        self.showH264_th.start()
        self.showH264_thremal_th = threading.Thread(target=self.show_H264_thremal_picture)
        self.showH264_thremal_th.start()
        self.warning_th = threading.Thread(target=self.warning_info_manager)
        self.warning_th.start()

        # 飞控数据存储标志 True 保存，False 不保存
        self.flyData_storage_flag = False
        # 是否有目标信息

        # MCM与复眼指令  可以直接 'new指令':0x00 添加对应指令
        self.mc_commands = {'重启系统': 0x80, '重启datadeliver': 0x40,
                            '重启MCM': 0x20, '重启micROS': 0x01,
                            '切入下一任务': 0x02, '上传目标位置': 0x03}
        self.eye_commands = {'吊舱软启动命令': 0x14, '吊舱启动图像采集命令': 0x15,
                             '吊舱停止图像采集命令': 0x16, '坐标信息': 0x18,
                             'camera选择': 0x19}

        # 飞控Excel列名，顺序不能变
        self.flydata_columns = ['psi', 'pit', 'rol', 'v', 'h', 'v_gps', 'lat', 'lon',
                                'H_gps', 'Psi_gs', 'year', 'month', 'day', 'hour', 'minute', 'second',
                                'BY', 'v_east', 'v_north', 'v_up']

        self.statusbar.showMessage("——By cps version:2.6.2")
        self.comboBox_init()
        self.list_command_changed()

        self.lineEdit_HostIP.setText(get_host_ip())
        self.lineEdit_HostPort.setPlaceholderText("输入端口号")
        self.lineEdit_HostPort.setText(str(6281))

        self.lineEdit_TargetIP.setText("192.168.1.12")
        self.lineEdit_Target_Port.setText(str(6280))
        self.lineEdit_flyCount.setText("1")

        # 测试
        self.label_camera_show_id_1.setText("x")
        self.label_camera_show_id_2.setText("x")

        self.btn_connect_net.clicked.connect(self.btn_connect_net_cb)
        self.btn_command_send.clicked.connect(self.btn_command_send_cb)
        self.btn_command_send_test.clicked.connect(self.btn_command_send_test_cb)
        self.btn_add_flyCount.clicked.connect(self.btn_add_flyCount_cb)
        self.btn_down_flyCount.clicked.connect(self.btn_down_flyCount_cb)
        self.comboBox_Machine_Type.currentIndexChanged.connect(self.list_command_changed)
        self.listView_command_list.clicked.connect(self.command_choosed_cb)
        self.rbtn_flydata_storgae.toggled.connect(self.rbtn_flydata_storage_cb)
        self.btn_saveflyData_iotdb.clicked.connect(self.btn_saveflyData_iotdb_cb)
        self.btn_show_rgb.clicked.connect(self.btn_show_rgb_cb)
        self.btn_show_thremal.clicked.connect(self.btn_show_thremal_cb)
        self.btn_choose_confirm.clicked.connect(self.btn_choose_confirm_cb)
        # self.label_picture_rbg.connect_customized_slot(self.btn_show_rgb_cb)

    def btn_choose_confirm_cb(self):
        uav_id = self.spinBox_uav_id.text()
        camera_id = self.spinBox_camera_id.text()

        self.textBrowser_StatusInfo.append(
            "<font color='blue'>已选择：无人机ID：" + uav_id + "  相机ID：" + camera_id + "</font>")
        self.command_choosed_cb(self.listView_command_list.currentIndex())

    def warning_info_manager(self):
        while True:
            current_path = Path(__file__).resolve()
            parent_path = current_path.parent.parent
            green_path = parent_path / "UI" / "img" / "warn_green.png"
            red_path = parent_path / "UI" / "img" / "warn_red.png"

            pixmap_green = QPixmap(str(green_path))
            pixmap_red = QPixmap(str(red_path))
            width = self.label_warn_pic.width()
            height = self.label_warn_pic.height()
            self.label_warn_pic.setAlignment(Qt.AlignHCenter)

            if not self.is_target_info:
                sca = pixmap_green.scaled(width, height, Qt.KeepAspectRatio)
                self.label_warn_pic.setPixmap(sca)
                self.label_warn_info.setText("未发现目标")
            else:
                sca = pixmap_red.scaled(width, height, Qt.KeepAspectRatio)
                self.label_warn_pic.setPixmap(sca)
                for i in range(2):
                    self.label_warn_info.setText("已发现目标")
                    time.sleep(0.2)
                    self.label_warn_info.setText("")
                    time.sleep(0.2)

            time.sleep(0.5)



    def btn_show_rgb_cb(self):
        """
        RGB图像显示
        :return:
        """
        print("显示RGB图片")
        path = self.current_picture_path
        img = Image.open(path)
        img.show()

    def btn_show_thremal_cb(self):
        """
        热成像图像显示
        :return:
        """
        print("显示热成像图片")
        path = self.current_picture_path_thremal
        img = Image.open(path)
        img.show()

    def btn_saveflyData_iotdb_cb(self):
        """
        # TODO
        将保存飞控数据的Excel保存至IotDB中

        自定义device device_id等关于数据库的选项
        :return:
        """
        device = "root.test2"
        device_id = device + ".uav1"
        excel_file = self.flyData_filePath

        username_ = "root"
        password_ = "root"
        ip = "127.0.0.1"
        port_ = 6667
        maxSize = 100

        iotdb_test = IotDB_utils(ip, port_, username_, password_)
        iotdb_test.IotDB_insert(excel_file, device, device_id)

        self.textBrowser_StatusInfo.append(
            f"<font color='blue'>飞控数据{self.flyData_filePath}保存数据库 success</font>")

    def btn_add_flyCount_cb(self):
        """
        增加试飞次数
        :return:
        """
        index = self.lineEdit_flyCount.text()
        index = str(int(index) + 1)
        self.lineEdit_flyCount.setText(index)

    def btn_down_flyCount_cb(self):
        """
        减少试飞次数
        :return:
        """
        index = self.lineEdit_flyCount.text()
        temp = int(index) - 1
        if temp < 0:
            temp = 0
        index = str(temp)
        self.lineEdit_flyCount.setText(index)

    def btn_command_send_test_cb(self):
        flag = False
        list = get_test_command(32)
        hex_string = ''.join([hex_value[2:] for hex_value in list])
        print(hex_string)
        if self.send_state is not None:
            # 启动发送线程，不影响主线程运行
            send_thread = threading.Thread(target=self.udp_send_bytes, args=(list,))
            send_thread.start()
            send_thread.join()
        else:
            self.textBrowser_StatusInfo.append("check connect state...")
            return
        if self.send_ret > 0:  # 似乎有些多余
            flag = True
        else:
            flag = False

        if flag:
            self.textBrowser_StatusInfo.append(f"<font color='green'>Ground Send test_data Success!</font>")
            self.textBrowser_StatusInfo.append(get_current_time())
            self.textBrowser_StatusInfo.append(str(hex_string))
            self.textBrowser_StatusInfo.moveCursor(self.textBrowser_StatusInfo.textCursor().End)
        else:
            self.textBrowser_StatusInfo.append("check connect state...")

    def rbtn_flydata_storage_cb(self):
        """
        飞控数据保存按键
        :return:
        """
        print("fly data storage")
        index = self.lineEdit_flyCount.text()
        # TODO 有效性判断
        # 小于0无效
        if int(index) < 0:
            print(f"无效试飞: {index}")
            return

        self.textBrowser_StatusInfo.append(f"fly data storage choose,  data/fly_data_{index}.xlsx")
        self.flydata_columns = ['psi', 'pit', 'rol', 'v', 'h', 'v_gps', 'lat', 'lon',
                                'H_gps', 'Psi_gs', 'year', 'month', 'day', 'hour', 'minute', 'second',
                                'BY', 'v_east', 'v_north', 'v_up']
        # 设置文件存储名
        file_name = "fly_data_" + index + ".xlsx"
        print(file_name)
        # 获取文件存储路径 GroundStation/data/fly_data_x.xlsx
        self.flyData_filePath = make_excel(columns=self.flydata_columns, name=file_name)
        if self.flyData_filePath is not None:
            self.flyData_storage_flag = True
            print("文件创建成功")
        else:
            self.flyData_storage_flag = False
            print("文件创建失败")

    def btn_connect_net_cb(self):
        """
        网络连接
        :return:
        """
        print("connect net")
        if self.comboBox_type.currentText() == "UDP":
            self.textBrowser_StatusInfo.append("UDP Server Start...")
            self.send_state = self.udp_send_init()
            self.recv_state = self.udp_recv_init_test()
            self.recv_h264_state = self.udp_recv_h264_init_test()
            self.recv_h264_thremal_state = self.udp_recv_h264_thermal_init()
            self.recv_all_96pkgs_state = self.udp_recv_all_96pkgs_init()

            if self.send_state is not None:
                self.textBrowser_StatusInfo.append("指令发送初始化完成")
                print("指令发送初始化完成")
            if self.recv_state is not None:
                self.textBrowser_StatusInfo.append("指令接收初始化完成")
                print("指令接收初始化完成")
            if self.recv_h264_state is not None:
                self.textBrowser_StatusInfo.append("接收h264 rgb初始化完成")
                print("接收h264 rgb初始化完成")
            if self.recv_h264_thremal_state is not None:
                self.textBrowser_StatusInfo.append("接收h264 thermal初始化完成")
                print("接收h264 thermal初始化完成")
            if self.recv_all_96pkgs_state is not None:
                self.textBrowser_StatusInfo.append("接收所有96字节初始化完成")
                print("接收所有96字节初始化完成")

    def btn_command_send_cb(self):
        """
        指令发送
        通过类型MCmanager CompoundEye判断
        :return:
        """
        print("send command")
        flag = False

        machine = self.comboBox_Machine_Type.currentText()
        command_list = self.command_choosed_cb(self.listView_command_list.currentIndex())
        print(command_list)

        ret = -1
        if self.send_state is not None:
            # 启动发送线程，不影响主线程运行
            send_thread = threading.Thread(target=self.udp_send_bytes, args=(command_list,))
            send_thread.start()
            send_thread.join()
        else:
            self.textBrowser_StatusInfo.append("check connect state...")
            return
        if self.send_ret > 0:  # 似乎有些多余
            flag = True
        else:
            flag = False

        if flag:
            # self.textBrowser_StatusInfo.append(get_current_time())
            self.textBrowser_StatusInfo.append(f"<font color='green'>Ground Send Command to {machine} Success!</font>")
            self.textBrowser_StatusInfo.append(str(command_list))
            self.textBrowser_StatusInfo.moveCursor(self.textBrowser_StatusInfo.textCursor().End)
            QApplication.processEvents()
        else:
            self.textBrowser_StatusInfo.append("check connect state...")

    def list_command_changed(self):
        # mc_list = ['重启系统','重启datadeliver','重启MCM','重启micROS','切入下一任务','上传目标位置']
        mc_list = list(self.mc_commands.keys())
        eye_list = list(self.eye_commands.keys())

        slm = QStringListModel()

        type = self.comboBox_Machine_Type.currentText()
        if type == "MCManger":
            slm.setStringList(mc_list)
        elif type == "CompoundEye":
            slm.setStringList(eye_list)

        self.listView_command_list.setModel(slm)

    def command_choosed_cb(self, index):
        machine = self.comboBox_Machine_Type.currentText()
        command_byte = 0x00

        mc_list = list(self.mc_commands.values())
        eye_list = list(self.eye_commands.values())

        if machine == "MCManger":
            command_byte = mc_list[index.row()]
        elif machine == "CompoundEye":
            command_byte = eye_list[index.row()]

        uav_id = int(self.spinBox_uav_id.value())
        camera_id = int(self.spinBox_camera_id.value())

        result = get_send_full_command(machine, command_byte, uav_id, camera_id)

        self.textEdit_command.clear()
        self.textEdit_command.append(str(result))

        return result

    def comboBox_init(self):
        list_net_type = ["UDP", "TCP"]
        list_machine_type = ["CompoundEye", "MCManger"]

        self.comboBox_type.addItems(list_net_type)
        self.comboBox_Machine_Type.addItems(list_machine_type)
        self.spinBox_uav_id.setRange(1, 3)
        self.spinBox_camera_id.setRange(1, 6)

    def udp_recv_init_test(self):
        """
        测试UDP接收
        :return:
        """

        ANY = '192.168.13.20'
        Multicast_address = "226.3.6.5"
        PORT = 6281
        try:
            self.udp_socket_recv_test = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
            # 允许端口复用
            self.udp_socket_recv_test.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
            # 绑定多播数据包的端口
            self.udp_socket_recv_test.bind((ANY, PORT))
            # 声明该socket为多播类型
            self.udp_socket_recv_test.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_TTL, 255)
            # 加入多播组
            self.udp_socket_recv_test.setsockopt(
                socket.IPPROTO_IP,
                socket.IP_ADD_MEMBERSHIP,
                socket.inet_aton(Multicast_address) + socket.inet_aton(ANY)
            )
            self.recv_thread_test = threading.Thread(target=self.udp_recv_listen_test)
            self.recv_thread_test.start()
        except Exception as e:
            print(e)
        return self.udp_socket_recv_test

    def udp_recv_h264_init_test(self):
        """
        测试UDP接收
        :return:
        """

        ANY = '192.168.13.20'
        Multicast_address = "226.3.6.5"
        PORT = 6282
        try:
            self.udp_socket_recv_h264_test = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
            # 允许端口复用
            self.udp_socket_recv_h264_test.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
            # 绑定多播数据包的端口
            self.udp_socket_recv_h264_test.bind((ANY, PORT))
            # 声明该socket为多播类型
            self.udp_socket_recv_h264_test.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_TTL, 255)
            # 加入多播组
            self.udp_socket_recv_h264_test.setsockopt(
                socket.IPPROTO_IP,
                socket.IP_ADD_MEMBERSHIP,
                socket.inet_aton(Multicast_address) + socket.inet_aton(ANY)
            )
            self.recv_h264_thread_test = threading.Thread(target=self.udp_recv_h264_listen_test)
            self.recv_h264_thread_test.start()
        except Exception as e:
            print(e)
        return self.udp_socket_recv_h264_test

    def udp_recv_all_96pkgs_init(self):
        """
        接收复眼发送过来的所有96字节信息
        新建端口号 6383
        :return:
        """
        ANY = '192.168.13.20'
        Multicast_address = "226.3.6.5"
        PORT = 6383
        PORT2 = 6384
        try:
            self.udp_socket_recv_all_96pkgs = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
            # 允许端口复用
            self.udp_socket_recv_all_96pkgs.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
            # 绑定多播数据包的端口
            self.udp_socket_recv_all_96pkgs.bind((ANY, PORT))
            # 声明该socket为多播类型
            self.udp_socket_recv_all_96pkgs.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_TTL, 255)
            # 加入多播组
            self.udp_socket_recv_all_96pkgs.setsockopt(
                socket.IPPROTO_IP,
                socket.IP_ADD_MEMBERSHIP,
                socket.inet_aton(Multicast_address) + socket.inet_aton(ANY)
            )

            self.udp_socket_recv_all_96pkgs2 = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
            # 允许端口复用
            self.udp_socket_recv_all_96pkgs2.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
            # 绑定多播数据包的端口
            self.udp_socket_recv_all_96pkgs2.bind((ANY, PORT2))
            # 声明该socket为多播类型
            self.udp_socket_recv_all_96pkgs2.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_TTL, 255)
            # 加入多播组
            self.udp_socket_recv_all_96pkgs2.setsockopt(
                socket.IPPROTO_IP,
                socket.IP_ADD_MEMBERSHIP,
                socket.inet_aton(Multicast_address) + socket.inet_aton(ANY)
            )
            self.recv_thread_all_96pkgs = threading.Thread(target=self.upd_recv_all_96pkgs_listen)
            self.recv_thread_all_96pkgs.start()
        except Exception as e:
            print(e)
        return self.recv_thread_all_96pkgs

    def upd_recv_all_96pkgs_listen(self):
        while True:
            recv_data, recv_addr = self.udp_socket_recv_all_96pkgs.recvfrom(1024)
            data_size = len(recv_data)

            recv_data2, recv_addr2 = self.udp_socket_recv_all_96pkgs2.recvfrom(1024)
            # 暂时使用thermal存放所有的96字节数据
            if data_size == 96:
                target_num, camera_id = data_96bytes_parser("thermal", recv_data, self.count_all_96pkgs)
                target_num2, camera_id2 = data_96bytes_parser("thermal", recv_data2, self.count_all_96pkgs)
                if target_num + target_num2 > 0:
                    self.textBrowser_FlyData.append("已发现目标！")
                    self.textBrowser_FlyData.moveCursor(self.textBrowser_FlyData.textCursor().End)
                    self.is_target_info = True
                else:
                    self.textBrowser_FlyData.append("未发现目标")
                    self.textBrowser_FlyData.moveCursor(self.textBrowser_FlyData.textCursor().End)
                    self.is_target_info = False
                self.count_all_96pkgs = self.count_all_96pkgs + 1

    def udp_recv_h264_thermal_init(self):
        """
        测试UDP接收热红外图像
        :return:
        """

        ANY = '192.168.13.20'
        Multicast_address = "226.3.6.5"
        PORT = 6283
        try:
            self.udp_socket_recv_h264_thermal = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
            # 允许端口复用
            self.udp_socket_recv_h264_thermal.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
            # 绑定多播数据包的端口
            self.udp_socket_recv_h264_thermal.bind((ANY, PORT))
            # 声明该socket为多播类型
            self.udp_socket_recv_h264_thermal.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_TTL, 255)
            # 加入多播组
            self.udp_socket_recv_h264_thermal.setsockopt(
                socket.IPPROTO_IP,
                socket.IP_ADD_MEMBERSHIP,
                socket.inet_aton(Multicast_address) + socket.inet_aton(ANY)
            )
            self.udp_thread_recv_h264_thermal = threading.Thread(target=self.udp_recv_h264_thermal_listen)
            self.udp_thread_recv_h264_thermal.start()
        except Exception as e:
            print(e)
        return self.udp_socket_recv_h264_thermal

    def udp_recv_h264_listen_test(self):
        while True:
            pkgs_num = 0  # 1024字节包的数量

            file_saved = False
            temp_list = []

            recv_data, recv_addr = self.udp_socket_recv_h264_test.recvfrom(1024)
            data_size = len(recv_data)
            if data_size < 0:
                print("接收数据失败")
                continue
            # msg = f"来自IP:{recv_addr[0]}端口:{recv_addr[1]}:"
            # print(msg)
            if data_size == 16:
                if recv_data[0] == 0x9F and recv_data[1] == 0xE8 and recv_data[2] == 0x9F and recv_data[3] == 0xE7:
                    print(Bytes_HexStr(recv_data))
                    pkgs_num = recv_data[4] << 8 | recv_data[5]
                    self.current_rgb_pkgs_remind = recv_data[6] << 8 | recv_data[7]
                    sum = pkgs_num * 1024 + self.current_rgb_pkgs_remind
                    print(f"rgb listen: 数据包数量{pkgs_num}, 最后包大小{self.current_rgb_pkgs_remind},总大小{sum}")
                    file_saved = True
                    self.video_data_queue.put(recv_data)

            if data_size == 96:
                if recv_data[0] == 0x9F and recv_data[1] == 0xE4 and recv_data[2] == 0x9F and recv_data[3] == 0xE4:
                    self.video_data_queue.put(recv_data)
            if data_size == 1024:
                self.video_data_queue.put(recv_data)
            if data_size == self.current_rgb_pkgs_remind:
                self.video_data_queue.put(recv_data)
                self.condition_saved.acquire()
                self.condition_saved.notify()
                self.condition_saved.release()

    def udp_recv_h264_thermal_listen(self):
        while True:
            pkgs_num = 0  # 1024字节包的数量
            file_saved = False
            temp_list = []

            recv_data, recv_addr = self.udp_socket_recv_h264_thermal.recvfrom(1024)
            data_size = len(recv_data)
            if data_size < 0:
                print("接收数据失败")
                continue
            # msg = f"来自IP:{recv_addr[0]}端口:{recv_addr[1]}:"
            # print(msg)
            if data_size == 16:
                if recv_data[0] == 0x9F and recv_data[1] == 0xE8 and recv_data[2] == 0x9F and recv_data[3] == 0xE7:
                    print(Bytes_HexStr(recv_data))
                    pkgs_num = recv_data[4] << 8 | recv_data[5]
                    self.current_thermal_pkgs_remind = recv_data[6] << 8 | recv_data[7]
                    sum = pkgs_num * 1024 + self.current_thermal_pkgs_remind

                    print(
                        f"thermal listen: {self.count_16bytes}，数据包数量{pkgs_num}, 最后包大小{self.current_thermal_pkgs_remind},总大小{sum}")
                    file_saved = True
                    self.count_16bytes = self.count_16bytes + 1
                    self.video_thermal_data_queue.put(recv_data)

            if data_size == 96:
                if recv_data[0] == 0x9F and recv_data[1] == 0xE4 and recv_data[2] == 0x9F and recv_data[3] == 0xE4:
                    self.video_thermal_data_queue.put(recv_data)
            if data_size == 1024:
                self.video_thermal_data_queue.put(recv_data)
            if data_size == self.current_thermal_pkgs_remind:
                self.video_thermal_data_queue.put(recv_data)
                self.condition_saved_thermal.acquire()
                self.condition_saved_thermal.notify()
                self.condition_saved_thermal.release()

    def udp_recv_listen_test(self):
        """
        UDP网络接收监听测试
        :return:
        """
        while True:
            try:
                recv_data, recv_addr = self.udp_socket_recv_test.recvfrom(1024)
                # info = recv_msg.decode("utf-8")
                data_size = len(recv_data)
                if data_size < 0:
                    print("接收数据失败")
                    continue
                # msg = f"来自IP:{recv_addr[0]}端口:{recv_addr[1]}:"
                # print(msg)
                if data_size == 64:
                    # 飞控帧头 0x9F 0xE4
                    if recv_data[0] == 0x9F:
                        if recv_data[1] == 0xE4:
                            print("fly data")
                            self.flyData_queue_test.put(recv_data)
                            self.textBrowser_FlyData.append(get_current_time())
                            self.textBrowser_FlyData.append(Bytes_HexStr(recv_data))
                            self.textBrowser_FlyData.moveCursor(self.textBrowser_FlyData.textCursor().End)

                elif data_size == 32:
                    print("command")
                    if recv_data[0] == 0x9F:
                        # 0xE7 感算一体机状态回报 0xE4 复眼状态回报
                        if recv_data[1] == 0xE7 or recv_data[1] == 0xE4:
                            QApplication.processEvents()
                            self.command_queue_test.put(recv_data)
                            # self.textBrowser_StatusInfo.append(get_current_time())
                            self.textBrowser_StatusInfo.append(Bytes_HexStr(recv_data))
                            self.textBrowser_StatusInfo.moveCursor(self.textBrowser_StatusInfo.textCursor().End)
                            # QApplication.processEvents()
                elif data_size == 16:
                    print("test")
                    self.textBrowser_StatusInfo.append(f"<font color='red'>Ground Recv test_data Success!</font>")
                    self.textBrowser_StatusInfo.append(get_current_time())
                    hex_str = ''.join(f'{byte:02x}' for byte in recv_data).upper()
                    self.textBrowser_StatusInfo.append(hex_str)
                    self.textBrowser_StatusInfo.moveCursor(self.textBrowser_StatusInfo.textCursor().End)

            except Exception as e:
                print(e)
                break

    def dataParser_rgb_picture(self):
        """
        图片解析，根据16字节控制信息解析文件
        0   1  2  3    4             5           6          7       8-14   15
        9F E8 9F E7 包数量高八位 包数量低八位 剩余数高八位 剩余数低八位 无 校验和
        :return:
        """
        while True:
            # 等待有图片数据进入
            self.condition_saved.acquire()
            self.condition_saved.wait()
            self.condition_saved.release()

            list_temp_video = []
            pkgs_num = 0
            pkgs_remind = 0
            new_file = None
            sum = 0

            while True:

                recv_data = self.video_data_queue.get()
                if len(recv_data) == 16:
                    if recv_data[0] == 0x9F and recv_data[1] == 0xE8 and recv_data[2] == 0x9F and recv_data[3] == 0xE7:
                        pkgs_num = recv_data[4] << 8 | recv_data[5]
                        pkgs_remind = recv_data[6] << 8 | recv_data[7]
                        sum = pkgs_num * 1024 + pkgs_remind
                        print(f"dataParser_rgb:数据包数量{pkgs_num}, 最后包大小{pkgs_remind},总大小{sum}")
                        self.mcm_id = recv_data[8]
                        if self.mcm_id > 0:
                            self.textBrowser_FlyData.append(f"{self.mcm_id} mcm get data showing ")
                            print(f"{self.mcm_id} mcm get data showing ")
                        self.textBrowser_FlyData.moveCursor(self.textBrowser_FlyData.textCursor().End)
                        # 新建文件  文件名self.FileIndex 新建文件至captureVideo/rgb目录下
                        current_path = Path(__file__).resolve()
                        parent_path = current_path.parent.parent
                        folder = parent_path / "captureVideo" / "rgb"
                        if not folder.exists():
                            folder.mkdir()
                        new_file = folder / str(self.FileIndex)
                        if not new_file.exists():
                            new_file.touch()
                if len(recv_data) == 96:
                    if recv_data[0] == 0x9F and recv_data[1] == 0xE4 and recv_data[2] == 0x9F and recv_data[3] == 0xE4:
                        # 飞控数据 目标信息数据 保存至队列中
                        # 使用正在展示的图片文件索引，保存到data/rgb_fly_data目录下
                        self.data_96bytes_rgb_queue.put(recv_data)
                if len(recv_data) == 1024:
                    list_temp_video.append(recv_data)

                if len(recv_data) == pkgs_remind:
                    list_temp_video.append(recv_data)
                    break

            with open(new_file, "ab") as file:
                for data in list_temp_video:
                    file.write(data)
            print(f"SavePictureParser_rgb:{new_file} 大小 {sum} saved")

            # 有图像原数据保存，通知显示线程
            self.FileIndex = self.FileIndex + 1
            with self.condition_show_h264:
                self.condition_show_h264.notify()
            # self.event_saved.set()

    def dataParser_thermal_picture(self):
        """
        图片解析，根据16字节控制信息解析文件
        0   1  2  3    4             5           6          7       8-14   15
        9F E8 9F E7 包数量高八位 包数量低八位 剩余数高八位 剩余数低八位 无 校验和
        :return:
        """
        while True:
            # 等待有图片数据进入
            self.condition_saved_thermal.acquire()
            self.condition_saved_thermal.wait()
            self.condition_saved_thermal.release()

            list_temp_video = []
            pkgs_num = 0
            pkgs_remind = 0
            new_file = None
            sum = 0

            while True:

                recv_data = self.video_thermal_data_queue.get()
                if len(recv_data) == 16:
                    if recv_data[0] == 0x9F and recv_data[1] == 0xE8 and recv_data[2] == 0x9F and recv_data[3] == 0xE7:
                        pkgs_num = recv_data[4] << 8 | recv_data[5]
                        pkgs_remind = recv_data[6] << 8 | recv_data[7]
                        sum = pkgs_num * 1024 + pkgs_remind
                        print(f"dataParser_thermal:数据包数量{pkgs_num}, 最后包大小{pkgs_remind},总大小{sum}")

                        # 新建文件  文件名self.FileIndex 新建文件至captureVideo/rgb目录下
                        current_path = Path(__file__).resolve()
                        parent_path = current_path.parent.parent
                        folder = parent_path / "captureVideo" / "thermal"
                        if not folder.exists():
                            folder.mkdir()
                        new_file = folder / str(self.FileIndex_thermal)
                        if not new_file.exists():
                            new_file.touch()
                if len(recv_data) == 96:
                    if recv_data[0] == 0x9F and recv_data[1] == 0xE4 and recv_data[2] == 0x9F and recv_data[3] == 0xE4:
                        # 飞控数据 目标信息数据 保存至队列中
                        # 使用正在展示的图片文件索引，保存到data/rgb_fly_data目录下
                        self.data_96bytes_thermal_queue.put(recv_data)
                if len(recv_data) == 1024:
                    list_temp_video.append(recv_data)
                if len(recv_data) == pkgs_remind:
                    list_temp_video.append(recv_data)
                    break

            with open(new_file, "ab") as file:
                for data in list_temp_video:
                    file.write(data)
            print(f"SavePictureParser_thermal:{new_file} 大小 {sum} saved")

            # 有图像原数据保存，通知显示线程
            self.FileIndex_thermal = self.FileIndex_thermal + 1
            with self.condition_show_thermal_h264:
                self.condition_show_thermal_h264.notify()
            # self.event_thermal_saved.set()

    def show_H264_picture(self):
        """
        将H264图像显示
        :return:
        """
        # self.event_saved.wait()

        while True:
            # 等待有图片数据进入
            with self.condition_show_h264:
                self.condition_show_h264.wait()
            try:
                current_path = Path(__file__).resolve()
                parent_path = current_path.parent.parent

                # 获取当前图片路径
                # temp_index = self.FileIndex - 1
                # filename = str(temp_index)
                self.show_FileIndex = self.FileIndex - 1
                filename = str(self.show_FileIndex)
                folder = parent_path / "captureVideo" / "rgb" / filename

                # print(str(folder))
                i = 0
                frame_ratio = 2  # 设置抽帧比例  eg. 2 每两帧抽帧一次

                # opencv解析图片
                capture = cv2.VideoCapture(str(folder), cv2.CAP_FFMPEG)
                width = self.label_picture_rbg.width()
                height = self.label_picture_rbg.height()
                self.label_picture_rbg.setAlignment(Qt.AlignHCenter)

                # TODO 7.4 ZhenYu 明天测试直接使用当前的FileIndex-1的值进行显示。
                print(f"frame_{str(self.show_FileIndex)} start to save")

                while True:
                    img = "frame_" + str(self.show_FileIndex) + "_" + str(i) + ".jpg"
                    img_name = parent_path / "img" / "rgb" / img
                    ret, frame = capture.read()
                    if frame is None:
                        break
                    i = i + 1
                    if i % frame_ratio == 0:
                        # # 文件保存
                        # cv2.imwrite(str(img_name), frame)
                        # # 获取当前图片路径
                        # self.current_picture_path = str(img_name)
                        # # 在label中显示图片 需按比例缩放图片
                        # pixmap = QPixmap(self.current_picture_path)
                        # # 按比例缩放
                        # sca = pixmap.scaled(width, height, Qt.KeepAspectRatio)
                        # self.label_picture_rbg.setPixmap(sca)
                        # 将 BGR 转换为 RGB
                        frame_rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
                        # 获取图像的高度、宽度和通道数
                        height, width, channel = frame_rgb.shape
                        # 创建 QImage
                        q_img = QImage(frame_rgb.data, width, height, width * channel, QImage.Format_RGB888)
                        # 将 QImage 转换为 QPixmap
                        pixmap = QPixmap.fromImage(q_img)
                        # 计算缩放后的大小（假设你有预定的 QLabel 的宽度和高度）
                        label_width = self.label_picture_rbg.width()
                        label_height = self.label_picture_rbg.height()
                        sca = pixmap.scaled(label_width, label_height, Qt.KeepAspectRatio)
                        # 将 pixmap 显示在 QLabel 中
                        self.label_picture_rbg.setPixmap(sca)
                # 同步保存96字节信息
                self.rgb_target_nums = 0
                temp_data = self.data_96bytes_rgb_queue.get()

                self.rgb_target_nums, camera_id = data_96bytes_parser("rgb", temp_data, self.show_FileIndex)
                print(f"show_rgb: thermal_target_nums:{self.thermal_target_nums}")
                print(f"show_rgb: rgb_target_nums:{self.rgb_target_nums}")
                if self.rgb_target_nums + self.thermal_target_nums > 0:
                    self.textBrowser_FlyData.append("已发现目标")
                    self.textBrowser_FlyData.moveCursor(self.textBrowser_FlyData.textCursor().End)
                    self.is_target_info = True
                else:
                    self.textBrowser_FlyData.append("未发现目标")
                    self.textBrowser_FlyData.moveCursor(self.textBrowser_FlyData.textCursor().End)
                    self.is_target_info = False

                self.label_camera_show_id_1.setText(str(camera_id))
                print(f"frame_{str(self.show_FileIndex)} saved, 96bytes info {str(self.show_FileIndex)} saved")
                QApplication.processEvents()
                capture.release()

                self.show_FileIndex = self.show_FileIndex + 1

            except Exception as e:
                print("show_H264_picture: " + str(e) + "视频解析失败")
                time.sleep(2)

    def show_H264_thremal_picture(self):
        """
        将H264图像显示
        :return:
        """
        # self.event_thermal_saved.wait()

        while True:
            # 等待有图片数据进入
            with self.condition_show_thermal_h264:
                self.condition_show_thermal_h264.wait()
            try:
                current_path = Path(__file__).resolve()
                parent_path = current_path.parent.parent

                # 获取当前图片路径
                self.show_FileIndex_thermal = self.FileIndex_thermal - 1
                filename = str(self.show_FileIndex_thermal)
                folder = parent_path / "captureVideo" / "thermal" / filename

                i = 0
                frame_ratio = 2  # 设置抽帧比例  eg. 2 每两帧抽帧一次

                # opencv解析图片
                capture = cv2.VideoCapture(str(folder))
                width = self.label_picture_rbg.width()
                height = self.label_picture_rbg.height()
                self.label_picture_thremal.setAlignment(Qt.AlignHCenter)
                print(f"thermal: frame_{str(self.show_FileIndex_thermal)} start to save")

                while True:
                    img = "frame_" + str(self.show_FileIndex_thermal) + ".jpg"
                    img_name = parent_path / "img" / "thermal" / img
                    ret, frame = capture.read()
                    if frame is None:
                        break
                    i = i + 1
                    if i % frame_ratio == 0:
                        # cv2.imwrite(str(img_name), frame)
                        # # 获取当前图片路径
                        # self.current_picture_thermal_path = str(img_name)
                        # # 在label中显示图片 需按比例缩放图片
                        # pixmap = QPixmap(self.current_picture_thermal_path)
                        # # 按比例缩放
                        # sca = pixmap.scaled(width, height, Qt.KeepAspectRatio)
                        # self.label_picture_thremal.setPixmap(sca)
                        # 将 BGR 转换为 RGB
                        frame_rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
                        # 获取图像的高度、宽度和通道数
                        height, width, channel = frame_rgb.shape
                        # 创建 QImage
                        q_img = QImage(frame_rgb.data, width, height, width * channel, QImage.Format_RGB888)
                        # 将 QImage 转换为 QPixmap
                        pixmap = QPixmap.fromImage(q_img)
                        # 计算缩放后的大小（假设你有预定的 QLabel 的宽度和高度）
                        label_width = self.label_picture_thremal.width()
                        label_height = self.label_picture_thremal.height()
                        sca = pixmap.scaled(label_width, label_height, Qt.KeepAspectRatio)
                        # 将 pixmap 显示在 QLabel 中
                        self.label_picture_thremal.setPixmap(sca)
                # 同步保存96字节信息
                self.thermal_target_nums = 0
                temp_data = self.data_96bytes_thermal_queue.get()
                self.thermal_target_nums, camera_id = data_96bytes_parser("thermal", temp_data,
                                                                          self.show_FileIndex_thermal)

                print(f"show_thermal: thermal_target_nums:{self.thermal_target_nums}")
                print(f"show_thermal: rgb_target_nums:{self.rgb_target_nums}")
                if self.rgb_target_nums + self.thermal_target_nums > 0:
                    self.textBrowser_FlyData.append("已发现目标")
                    self.textBrowser_FlyData.moveCursor(self.textBrowser_FlyData.textCursor().End)
                    self.is_target_info = True
                else:
                    self.textBrowser_FlyData.append("未发现目标")
                    self.textBrowser_FlyData.moveCursor(self.textBrowser_FlyData.textCursor().End)
                    self.is_target_info = False

                self.label_camera_show_id_2.setText(str(camera_id))
                print(
                    f"thermal: frame_{str(self.show_FileIndex_thermal)} saved, 96bytes info {str(self.show_FileIndex_thermal)} saved")
                capture.release()
                QApplication.processEvents()

                # self.show_FileIndex_thermal = self.show_FileIndex_thermal + 1

            except Exception as e:
                print("show H264 thermal " + str(e))
                time.sleep(2)

    def dataParser_flyData(self):
        '''
        数据解析
        '''
        while True:

            # 飞控数据
            if not self.flyData_queue_test.empty():
                temp_flydata = self.flyData_queue_test.get()
                # 数据有效性判断 同步帧头 0x9F 0xE4
                if temp_flydata[0] == 0x9F and temp_flydata[1] == 0xE4:
                    # TODO 如果测试时，数据不正确，将pis pit等数组中的0和1交换位置
                    #   可以直接接不使用unpack

                    index_low = 1
                    index_high = 0

                    # 航向角 psi
                    psi = struct.unpack('bb', temp_flydata[22 - 1:23])
                    # psi = psi[index_low] << 8 | psi[index_high]
                    psi = psi[index_high]
                    psi = psi / 10.0

                    # 俯仰角 pit
                    pit = struct.unpack('bb', temp_flydata[24 - 1:25])
                    pit = pit[index_low] << 8 | pit[index_high]
                    pit = int(pit * 156 / 4095 - 78)

                    # 切斜角 rol
                    rol = struct.unpack('bb', temp_flydata[26 - 1:27])
                    rol = rol[index_low] << 8 | rol[index_high]
                    rol = int(rol * 156 / 4095 - 78)

                    # 空速 v 0.01m/s
                    v = struct.unpack('bb', temp_flydata[28 - 1:29])
                    v = v[index_low] << 8 | v[index_high]
                    v = v / 100.0

                    # 气压高度
                    h = struct.unpack('bb', temp_flydata[30 - 1:31])
                    h = h[index_low] << 8 | h[index_high]
                    h = h * 2.5 - 300

                    # GPS地速 0.01m/s
                    v_gps = struct.unpack('bb', temp_flydata[32 - 1:33])
                    v_gps = v_gps[index_low] << 8 | v_gps[index_high]
                    v_gps = v_gps / 100.0

                    # 维度 0.001 首位为标志位
                    lat = temp_flydata[34 - 1:37]
                    bits_lat_1 = lat[3] & 0x80 != 0
                    if bits_lat_1 is True:  # 首位0
                        lat = lat[3] << 24 | lat[2] << 16 | lat[1] << 8 | lat[0]
                        lat = -(lat / 1000.0) / 3600.0
                        lat = format(lat, '.9f')
                    else:  # 首位1 改为0在计算 带上+
                        buffer = bytearray(lat)
                        buffer[3] &= 0x7F
                        lat = buffer[3] << 24 | buffer[2] << 16 | buffer[1] << 8 | buffer[0]
                        lat = lat / 1000.0 / 3600.0
                        lat = format(lat, '.9f')

                    lon = temp_flydata[38 - 1:41]
                    bits_lon_1 = lon[3] & 0x80 != 0
                    if bits_lon_1 is True:  # 首位0
                        lon = lon[3] << 24 | lon[2] << 16 | lon[1] << 8 | lon[0]
                        lon = -(lon / 1000.0) / 3600.0
                        lon = format(lon, '.9f')

                    else:  # 首位1 改为0在计算 带上+
                        buffer = bytearray(lon)
                        buffer[3] &= 0x7F
                        lon = buffer[3] << 24 | buffer[2] << 16 | buffer[1] << 8 | buffer[0]
                        lon = lon / 1000.0 / 3600.0
                        lon = format(lon, '.9f')

                    # GPS高度
                    H_gps = struct.unpack('bb', temp_flydata[42 - 1:43])
                    H_gps = H_gps[index_low] << 8 | H_gps[index_high]
                    H_gps = H_gps * 1 - 1000

                    # 航迹角
                    Psi_gs = struct.unpack('bb', temp_flydata[44 - 1:45])
                    Psi_gs = Psi_gs[index_low] << 8 | Psi_gs[index_high]
                    Psi_gs = Psi_gs / 10.0

                    # 年月日
                    year = temp_flydata[46 - 1]
                    month = temp_flydata[47 - 1]
                    day = temp_flydata[48 - 1]

                    # 时分秒
                    hour = temp_flydata[49 - 1]
                    minute = temp_flydata[50 - 1]
                    second = temp_flydata[51 - 1]

                    # 起飞点海拔
                    BY = temp_flydata[52 - 1:53]
                    BY = BY[index_low] << 8 | BY[index_high]

                    # 无人机东向速度
                    v_east = struct.unpack('bb', temp_flydata[58 - 1:59])
                    v_east = v_east[index_low] << 8 | v_east[index_high]
                    v_east = v_east * 200 / 32768

                    # 无人机北向速度
                    v_north = struct.unpack('bb', temp_flydata[60 - 1:61])
                    v_north = v_north[index_low] << 8 | v_north[index_high]
                    v_north = v_north * 200 / 32768

                    # 无人机天向速度
                    v_up = struct.unpack('bb', temp_flydata[62 - 1:63])
                    v_up = v_up[index_low] << 8 | v_up[index_high]
                    v_up = v_up * 200 / 32768

                    data_list = [psi, pit, rol, v, h, v_gps, lat, lon,
                                 H_gps, Psi_gs, year, month, day, hour, minute, second,
                                 BY, v_east, v_north, v_up]

                    # TODO 数据有效性判断
                    if self.flyData_storage_flag is True and self.flyData_filePath is not None:
                        add_data_to_excel(columns=self.flydata_columns, values=data_list, path=self.flyData_filePath)

            else:
                # print("飞控数据队列为空，waiting")
                time.sleep(1)

    def dataParser_command(self):
        # 指令，状态回报数据
        while True:

            if not self.command_queue_test.empty():
                temp_ctlData = self.command_queue_test.get()
                # 控制信息解析
                # 数据有效性判断 同步帧头 0x9F 0xE7/0xE4
                if temp_ctlData[0] == 0x9F:
                    # 感算一体机状态回报
                    if temp_ctlData[1] == 0xE7:
                        # CPU利用率
                        CPU_U = temp_ctlData[2]
                        # CPU温度
                        CPU_T = temp_ctlData[3]
                        # NPU利用率
                        NPU_U = temp_ctlData[4]
                        # NPU温度
                        NPU_T = temp_ctlData[5]
                        # NPU功耗
                        NPU_P = temp_ctlData[6]
                        # GPU显存？
                        GPU_M = temp_ctlData[7]
                        # 内存使用情况
                        MEM = temp_ctlData[8]
                        # 磁盘使用情况
                        DISK = temp_ctlData[9]
                        # I/O接口状态
                        IOPORT_SW = temp_ctlData[10]
                        for i in range(8):
                            bit = temp_ctlData[10] & (1 << i)
                            # 有一位有问题
                            if bit == 1:
                                IOPORT_SW += 1
                        IOPORT_SW = 0

                        # datadeliver运行状态   0有效，1异常
                        DD = temp_ctlData[11]
                        DD = 0

                        # MCM运行状态
                        MCM = temp_ctlData[12]
                        MCM = 0

                        # CS运行状态
                        CS = temp_ctlData[13]

                        print(f"CPU_U {CPU_U}, MEM {MEM}, DISK {DISK}")
                        self.lineEdit_CPU_U.setText(str(CPU_U))
                        self.lineEdit_MEM.setText(str(MEM))
                        self.lineEdit_DISK.setText(str(DISK))
                        if MCM == 0:
                            self.lineEdit_MCM.setText("正常")
                            self.lineEdit_MCM.setStyleSheet("QLineEdit { color: green; }")
                        else:
                            self.lineEdit_MCM.setText("异常")
                            self.lineEdit_MCM.setStyleSheet("QLineEdit { color: red; }")
                        if IOPORT_SW == 0:
                            self.lineEdit_IOPROT.setText("正常")
                            self.lineEdit_IOPROT.setStyleSheet("QLineEdit { color: green; }")
                        else:
                            self.lineEdit_IOPROT.setText("异常")
                            self.lineEdit_IOPROT.setStyleSheet("QLineEdit { color: red; }")
                    # 复眼状态回报
                    if temp_ctlData[1] == 0xE4:
                        # 故障码
                        if temp_ctlData[2] == 0x51:
                            # 各位故障码标识
                            pass
            else:
                # print("指令数据队列为空，waiting")
                time.sleep(1)

    def closeEvent(self, event):
        reply = QMessageBox.question(self, '确认', '是否要退出程序？', QMessageBox.Yes | QMessageBox.No, QMessageBox.No)
        if reply == QMessageBox.Yes:
            try:

                if self.saveData_th is not None:
                    StopThreading.stop_thread(self.saveData_th)
                    self.saveData_th.join()
                if self.command_th is not None:
                    StopThreading.stop_thread(self.command_th)
                    self.command_th.join()

                if self.recv_thread_test is not None:
                    StopThreading.stop_thread(self.recv_thread_test)
                if self.recv_h264_thremal_state is not None:
                    StopThreading.stop_thread(self.udp_thread_recv_h264_thermal)
                if self.recv_h264_thread_test is not None:
                    StopThreading.stop_thread(self.recv_h264_thread_test)


            except Exception as e:
                print("closeEvent: " + str(e))
                pass
            event.accept()
        else:
            event.ignore()


if __name__ == '__main__':
    QtWidgets.QApplication.setAttribute(Qt.AA_EnableHighDpiScaling)
    app = QtWidgets.QApplication(sys.argv)
    window = Ui_Main()
    window.show()
    sys.exit(app.exec_())
